
#include <BPainter>
#include <BMouseEvent>
#include <BChangeEvent>
#include <BCursor>
#include <BApplication>
#include <BSystem>
#include <BScreen>

#include <BDialog>
#include "member_BDialog.h"
#include "member_BWidget.h"
#include "IWindow.h"
#include "IGlobal.h"
#include "IWindow.h"

using namespace BWE;

#define member						(*(member_BDialog*)_ptr)
#define member_allocate()			_ptr = new member_BDialog(this)
#define member_release()			delete (member_BDialog*)_ptr

BDialog::BDialog(BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setBorder(3);
	this->setPerchs(2, 23, 2, 2);
	this->setMargins(3, 3, 3, 3);
	this->hide();
}
BDialog::~BDialog()
{
	member_release();
}

int BDialog::execute()
{
	member.result = 0;
	this->show();
	this->popup();
	while (!app_done && this->visible())
	{
		App_CycleBegin();
		if (this->modal())
			App_Event(widget_member(this)->window);
		else
			App_Event();
		App_Update();
		App_Frame();
		App_CycleEnd();
	}
	return member.result;
}

void BDialog::done(int result)
{
	this->hide();
	member.result = result;
}

void BDialog::setResult(int result)
{
	member.result = result;
}
int BDialog::result() const
{
	return member.result;
}

void BDialog::showEvent(const BEvent& event)
{
	BWidget* root = this->root();
	if (root == this)
	{
		BSize hsize = this->sizeHint();
		if (this->width() < hsize.width())
		{
			this->setWidth(hsize.width());
		}
		if (this->height() < hsize.height())
		{
			this->setHeight(hsize.height());
		}
		int number = BSystem::GetScreenNumber();
		const BScreen* screen = BSystem::GetScreen(0);
		BRect screenRect(screen->size());
		BRect rect = screenRect.align(Align_Center, this->size());
		this->setRect(rect);
		return;
	}
	if (widget_member(this)->layout == 0)
	{
		BSize hsize = this->sizeHint();
		if (this->width() < hsize.width())
		{
			this->setWidth(hsize.width());
		}
		if (this->height() < hsize.height())
		{
			this->setHeight(hsize.height());
		}
		BRect abRect = this->absoluteRect();
		if (!root->absoluteRect().contain(abRect))
		{
			abRect = root->absoluteRect().align(Align_Center, this->size());
			this->setAbsoluteRect(abRect);
		}
	}
}

void BDialog::changeEvent(const BChangeEvent& event)
{
	if (event.change() == Change_Title)
	{
		this->fresh();
	}
	if (event.change() == Change_Style)
	{
		const BStyle* style = event.style();
		const BFont* font = style->font();
		if (font)
		{
			int fontHeight = font->height();
			int top = this->perch(Part_Top);
			if (top < fontHeight)
			{
				this->setPerch(Part_Top, fontHeight);
			}
		}

		const BValue& buttonSizeValue = style->value(Value_Title_Button_Size);
		if (buttonSizeValue.valid())
			member.buttonSize = buttonSizeValue;

		const BValue& iconSizeValue = style->value(Value_Title_Icon_Size);
		if (iconSizeValue.valid())
			member.iconSize = iconSizeValue;

		const BValue& alignValue = style->value(Value_Title_Align);
		if (alignValue.valid())
			member.titleAlign = alignValue;

		const BValue& spaceValue = style->value(Value_Title_Space);
		if (spaceValue.valid())
			member.titleSpace = spaceValue;

		BIcon* closeIcon = style->icon(Icon_Title_Button_Close);
		if (closeIcon)
			member.closeIcon = closeIcon;

		BIcon* titleIcon = style->icon(Icon_Title);
		if (titleIcon)
			member.titleIcon = titleIcon;

		BImage* titleImage = style->image(Image_Title);
		if (titleImage)
			member.titleImage = titleImage;

		member.dirtyRects = true;
		this->fresh();
	}
}
void BDialog::resizeEvent(const BEvent& event)
{
	member.dirtyRects = true;
	this->fresh();
}

void BDialog::freshEvent(const BEvent& event)
{
	if (member.dirtyRects)
	{
		member.freshRects();
		member.dirtyRects = false;
	}
}
void BDialog::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawClient();
	painter.drawPerch();

	if (painter.setColor(Color_Title))
	{
		painter.fillRect(member.titleRect);
	}
	if (member.titleImage)
	{
		painter.drawImage(member.titleRect, member.titleImage);
	}

	if (this->title().size() && painter.setColor(Color_Title_Text))
	{
		if (member.tr_title.size())
			painter.drawText(member.textRect, member.tr_title);
		else
			painter.drawText(member.textRect, this->title());
	}

	if (member.closeIcon)
	{
		State state = State_Normal;
		if (member.hoverClose)
		{
			state = State_Hovered;
			if (member.pressed)
				state = State_Pressed;
		}
		BImage* image = member.closeIcon->image(state);
		if (image)
		{
			painter.drawImage(member.closeRect, image);
		}
	}
	else
	{
		StyleRole role = Color_Title_Button;
		if (member.hoverClose)
		{
			role = Color_Title_Button_Hovered;
			if (member.pressed)
				role = Color_Title_Button_Pressed;
		}
		if (painter.setColor(role))
		{
			painter.fillRect(member.closeRect * 0.8);

			BRect lineRect = member.closeRect * 0.6;
			painter.setColor(255, 255, 255);
			painter.setLineSmooth(true);
			painter.setLineWidth(2);
			painter.drawLine(lineRect.leftTop(), lineRect.rightBottom());
			painter.drawLine(lineRect.leftBottom(), lineRect.rightTop());
		}
	}

	painter.drawMargin();
	painter.drawBorder();
}

void BDialog::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	member.hoverTitle = false;
	member.hoverClose = false;
	member.pressed = false;
}
void BDialog::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	member.hoverTitle = false;
	member.hoverClose = false;
	member.pressed = false;
	this->fresh();
}

void BDialog::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	Button button = mouseEvent.button();

	member.pressed = true;
	member.pressedPos = pos;

	this->fresh();
}
void BDialog::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	member.pressed = false;
	this->fresh();
}
void BDialog::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (member.pressed && member.mode)
	{
		BPoint offset = pos - member.pressedPos;
		BRect rect = this->rect();
		switch (member.mode)
		{
		case Resize_Left:
			rect.left() = rect.left() + offset.x();
			pos -= offset;
			break;
		case Resize_Top:
			rect.top() = rect.top() + offset.y();
			pos -= offset;
			break;
		case Resize_Right:
			rect.right() = (rect.right() + offset.x());
			break;
		case Resize_Bottom:
			rect.bottom() = (rect.bottom() + offset.y());
			break;
		}
		this->setRect(rect);
		member.pressedPos = pos;
		return;
	}

	if (member.pressed && member.hoverTitle)
	{
		BPoint offset = pos - member.pressedPos;
		this->move(offset);
		return;
	}
	bool hoverTitle = !member.closeRect.contain(pos) && member.titleRect.contain(pos);
	if (member.hoverTitle != hoverTitle)
	{
		member.hoverTitle = hoverTitle;
		this->fresh();
		return;
	}
	bool hoverClose = member.closeRect.contain(pos);
	if (member.hoverClose != hoverClose)
	{
		member.hoverClose = hoverClose;
		this->fresh();
		return;
	}

	member.mode = Resize_None;
	if (!this->cursor())
		this->setCursor(Cursor_None);
	CursorType cursor = Cursor_None;
	if (member.borderLeft.contain(pos))
	{
		member.mode = Resize_Left;
		cursor = Cursor_SizeHor;
	}
	if (member.borderTop.contain(pos))
	{
		member.mode = Resize_Top;
		cursor = Cursor_SizeVer;
	}
	if (member.borderRight.contain(pos))
	{
		member.mode = Resize_Right;
		cursor = Cursor_SizeHor;
	}
	if (member.borderBottom.contain(pos))
	{
		member.mode = Resize_Bottom;
		cursor = Cursor_SizeVer;
	}
	if (cursor != this->cursor()->type())
	{
		this->setCursor(cursor);
		//puts("Cursor Changed.");
	}
}

void BDialog::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (member.closeRect.contain(pos))
	{
		this->hide();
	}
}
void BDialog::mouseDoubleClickEvent(const BMouseEvent& event)
{
	BPoint pos = event.pos();
	Button button = event.button();
	if (button == Button_Left && member.titleRect.contain(pos))
	{
		if (widget_member(this)->window)
		{
			if (widget_member(this)->window->isMaxinumed())
				widget_member(this)->window->showNormal();
			else
				widget_member(this)->window->showMaxinum();
		}
		else if (this->parent())
		{
			BRect fullRect;
			BWidget* root = this->root();
			if (root && widget_member(root)->window && widget_member(root)->window->popupWidgets.contain(this))
			{
				fullRect = root->clientRect();
			}
			else
			{
				fullRect = parent()->clientRect();
			}

			if (this->width() > fullRect.width() * 0.96 && this->height() > fullRect.height() * 0.96)
			{
				this->setRect(member.normalRect);
			}
			else
			{
				member.normalRect = this->rect();
				this->setRect(fullRect);
			}
		}
	}
}
